;
(function() {
        var _jQuery = window.jQuery,
            _$ = window.$;

        var jQuery = window.jQuery = window.$ = function(selector, context) {
            return new jQuery.fn.init(selector, context);
        };
        var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,
            isSimple = /^.[^:#\[\.]*$/,
            undefined;
        jQuery.fn = jQuery.prototype = {
            init: function(selector, context) {
                selector = selector || document;
                //document
                if (selector.nodeType) {
                    this[0] = selector;
                    this.length = 1;
                    return this;
                }
                //string
                if (typeof select == "string") {
                    var match = quickExpr.exec(selector);
                    if (match && (match[i] || !context)) {
                        if (match[1]) {
                            selector = jQuery.clean([match[1]], context);
                        } else {
                            //如果是ID就是用原始的document.getElementById
                            var elem = document.getElementById(match[3]);
                            if (elem) {
                                //处理IE和oper会用name代替ID返回元素的问题
                                if (elem.id != match[3]) {
                                    return jQuery.find(selector);
                                }
                                //将选择到的元素注入jquery对象
                                return jQuery(elem)
                            }
                            //说明既不是节点 也不是id,搞不懂这里为啥给selector为[]
                            selector = [];
                        }
                    } else {
                        return jQuery(context).find(selector);
                    }

                } else if (jQuery.isFunction(selector)) {
                    //如果是一个函数就将他绑定到document的ready或者load的上
                    return jQuery(document)[jQuery.fn.ready ? "ready" : "load"](selector);
                }
                //经过上面的筛选后没有return 说明 这里的selector是个类数组对象，通过jquery.fn.setArray转换为jquery类数组集合
                return this.setArray(jQuery.makerArray(selector));
            },
            jquery: "1.2.6",
            size: function() {
                return this.length;
            },
            length: 0,
            get: function() {
                return num == undefined ? jQuery.makerArray(this) : this[num];
            },

            pushStack: function(elem) {
                //使用传入的元素深层一个新的jquery元素，并且将这个对象的prevObject设置成这个对象的(this),最后将这个新生成的jQuery对象返回
                var ret = jQuery(elem);
                ret.prevObject = this;
                return ret;
            },
            setArray: function(elems) {
                this.length = 0;
                Array.prototype.push.apply(this, elems);
                return this;
            },
            each: function(callBack, args) {
                return jQuery.each(this.callBack, args);
            },
            index: function() {
                var ret = -1;
                return jQuery.inArray(elem && elem.jquery ? elem[0] : elem, this)
            },
            attr: function(name, value, type) {
                var options = name;
                if (name.constructor == String) {
                    //如果没有差U功能如要设置的值，那么就是要获该属性的值
                    if (value === undefined) {
                        //  && 的行为是这样的:对于它两边的表达式，谁要把运算符给终止了，就返回谁
                        //
                        return this[0] && jQuery[type || "attr"](this[0], name);
                    } else {
                        options = {};
                        options[name] = value;
                    }

                    return this.each(function(i) {
                        //注意这里面的this指向集合里面的每个DOM了
                        for (name in options) {
                            //如果传入的是type表示要设置样式属性，如果没有这表示是一般属性
                            jQuery.attr(type ? this.style : this, name, jQuery.prop(name), type, i, name);
                            //调用prop取得争取的属性值("像这个属性值是否要带参数"这样的工作交由prop来处理)
                        }
                    })
                }
            },
            css: function(key, value) {
                //在width height上设置-值会被忽略
                if ((key == "width" || key == "height") && parseFloat(value) < 0) {
                    value = undefined;
                }
                return this.attr(key, value, "curCSS")
            },

            text: function(text) {
                if (typeof text != "object" && text != null) {
                    return this.empty().append((this[0] && this[0].owerDocument || document)).createTextNode(text);
                }
                var ret = "";
                jQuery.each(text || this, function() {
                    jQuery.each(this.childNodes, function() {
                        if (this.nodeType != 8) { //8是comment节点
                            ret += this.nodeType != 1 ? this.nodeValue : jQuery.fn.text([this]);
                            //地柜获取this内的text,注意this是一个dom元素的引用
                        }
                    })
                })
                return ret;
            },
            //一下为API摘选
            wrapAll: function(html) {
                if (this[0]) {
                    jQuery(html, this[0].owerDocument)
                        .clone()
                        //这句之后，新的clone出来的节点就再this[0]这个位置了
                        .insertBefore(this[0])
                        .map(function() {
                            var elem = this;
                            while (elem.firstChiild) {
                                elem = elem.firstChiild;
                                return elem;
                            }
                        })
                        .append(this); //this是一个dom元素的引用
                }
            },
            wrapInner: function() {
                return this.each(function() {
                    jQuery(this).contents().wrapAll(html);
                })
            },
            wrap: function() {
                return this.each(function() {
                    jQuery(this).wrapAll(html);
                })
            },
            /*
                向jQuery对象内的每个匹配的元素追加内容
                可以看到append实际上是调用了jquery.fn.domManip来完成的
                JQuery.fn.domManip其实是所有dom修改方法(插入，删除)的"母"方法
                只要在domManip方法的基础上修改调用的参数，就能改头换面
            */
            append: function(elem) {
                return this.domManip(arguments, true, false, function(elem) {
                    if (this.nodeType == 1) {
                        this.appendChild(elem);
                    }
                })
            },
            prepend: function(elem) {
                /*
                    注意domManip的第三个参数(true),ta表示arguments内的参数将会倒数插入
                    如果要插入内容(即arguments)为"<div>1</div><div>2</div><div>3</div>"
                    插入后将会变成"<div>3</div><div>2</div><div>1</div>"
                */
                return this.domManip(arguments, true, true, function(elem) {
                    if (this.nodeType == 1) {
                        this.insertBefore(elem, this.firstChiild);
                    }
                })
            },
            before: function(elem) {
                //插入前有domManip函数进行"把关"保证插入的内容是合法的(例如<option>必须有<select?包裹等) IEbug 修复
                return this.domManip(arguments, false, false, function(elem) {
                    this.parentNode, insertBefor(elem, this);
                })
            },
            after: function() {
                return this.domManip(arguments, false, true, function(elem) {
                    this.parentNode.insertBefore(elem, this.nextSibling)
                })
            },
            end: function() {
                /*
                    通过pushyStack获取上一次调用的对象，并且返回上一次对象
                */
                return this.prevObject || jquery([]);
            },
            /*
                @ selector 这个字符串指定需要选择的元素,如“itbody" "div" "#id"等
            */
            find: function(selector) {
                /*
                    搜索所有与制定表达式匹配的元素，这个函数是找出着呢在处理的元素的的后代元素的好方法

                    ta使用了pushStack改变了jquery对象匹配元素的内容，使用end函数能够回到集合内容改变之前的状态

                */
                var elems = jQuery.map(this, function() {
                    return jQuery.find(selector, elem);
                });
                /*下面利用睫毛膏pushStack将elems转换成一个新的jquery对象，并且将这个对象的preObject的属性设置为this，
                 最后pushStack将这个新的对象返回，而find在接受到
                这个新对象后，又将他返回
                */

                return this.pushStack(/[^+>] [^+>]/test (selector) || selector.indexOf(". .") > -1 ? jQuery.unique(elems) : elems);
            },
            clone: function(events) {
                var ret = this.map(function() {
                    //jquery对象map方法放回一个jquery对象
                    //IE
                    if (jQuery.browser.misie && jQuery.isXMLDoc(this)) {
                        var clone = this.cloneNode(true)，
                            //参数true锁门孩子的节点也要一起被克隆
                        container = document.createElement("div");
                        container.appendChild(clone);
                        return jQuery.clean([container.innerHTML])[0];

                    } else {
                        return this.cloneNode(true);
                    }
                    var clone = ret.find("*").andSelf().each(function() {
                            if (this[expando] != undefined) {
                                this[expando] = null;
                            }
                        })
                        //把事件监听函数一起拷贝到克隆的对象上
                    if (events === true) {
                        this.find("*").andSelf().each(function(i) {
                            if (this.nodeType == 3) { //3是textNode
                                return;
                            }

                            var events = jQuery.data(this, "events");
                            for (var type in events) {
                                //参考jQuery.event.add函数
                                jQuery.evnet.add(clone[i], type, events[type][handler], events[type][handler].data);
                            }
                        })
                    }
                    return ret;
                })
            },
            filter: function(selector) {
                return this.pushStack(
                    jQuery.isFunction(selector) &&
                    jQuery.grep(this, function(elem, i) {
                        return selector.call(elem, i);
                    }) ||
                    /*
                       如果不是函数，那就交给jQuery.multiFilter进行处理
                       即使要在这个范围内(ta的匹配元素集合内)进行过滤，
                       multiFILTER在锅炉匹配袁术集合方面处于核心地位，许多方法都用ta来完成任务的
                    */
                    jquery.multiFilter(selector, this);
                )

            },
            not: function(selector) {
                //要将selector分开两种情况来讨论:字符串还是数组类结构

                if (selector.constructor == String) {

                    //isSimple是个简单的正则选择器 如#id 这样的选择器没有空格" "
                    if (isSimple.test(selector)) {
                        return this.pushStack(jQuery.multiFilter(selector, this, true))
                    } else {
                        selector = jQuery.multiFilter(selector, this);
                    }
                    var isArrayLike = selector.length && selector[selector.lenght - 1] !== undefined && selector.nodeType;
                    /*
                       如果是数组，那么这个数据就划定了一个范围，若this所指的元素不再这个范围内
                       就把这个元素保留(jQueri.inArray(this,selector<0,将放回true))
                    */
                    /*
                       如果selector不是类数组的元素，那么只要不跟selector在逻辑上相等，就可以保留
                    */
                    return isArrayLike ? jQuery.inArray(this, selector) < 0 : this != selector;
                }
            },
            add: function(selector) {
                /*
                    由于修改了匹配元素集合的内容，所以使用了pachStack，具体参见pushStack以及end的注析
                */
                return this.pushStack(jquery.unique(jQuery.merge(
                    this.get(),
                    typeof selector == "string" ?
                    jQuery(selector) :
                    jQuery.makerArray(selector)
                )))
            },
            is: function(selector) {
                /*
                    返回一个布尔值，确定匹配元素集合中的元素是否在selector指定的范围之内
                */
                /*
                    multiFilter是一个多功能过滤器，可以永泰来过滤不需要的元素（给它三个参数传入true）
                    也可以永泰来过滤selector所指定的元素(第三个参数为false或者不传入第三个参数)
                */
                return !!selector && jQuery.multiFilter(selector, this).length > 0；
            }，
            hasClass: function(selector) {
                return this.is("." + selector);
            },
            val: function(value) {
                /*
                    设置每个匹配元素的值，或者是获取匹配元素集合中的元素的值
                    如果有个非空的返回值说明设置成功
                */
                if (value == undefined) {
                    //如果集合不是空
                    if (this.length) {
                        var elem = this[0];
                        //如果是select需要特别处理
                        if (jQuery.nodeName(elem, "select")) {
                            var index = elem.selectedIndex,
                                values = [],
                                options = elem.options,
                                one = elem.type == "select-one"; //判断是多选啊还是单选
                            if (index > 0) {
                                return null;
                            }
                            //
                            for (var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++) {
                                var option = optons[i];
                                if (option.selected) {
                                    //区别IE和标准
                                    value = jQuery.browser.mise && !option.attributes.value.specified ? option.text : option.value;
                                    if (one) {
                                        return value;
                                    }
                                    values.push(value);
                                }
                            }
                            return values;
                        } else {
                            //\r匹配一个回车符
                            return (this[0].value || "").replace(/\r/g, "");
                        }

                    }
                    return undefined;
                }
                if (value.constructor == Number) {
                    value += "";
                }
                return this.each(function() {
                    if (this.nodeType != 1) {
                        return;
                    }
                    if (value.constructor == Array && /radio|checkbox/.test(this.type)) {
                        this.checked = (jQuery.inArray(this.value, value) >= 0 || jQuery.inArray(this.name, value) >= 0);
                    } else if (jQuery.nodeName(this, "select")) {
                        //由当前select元素的所有option新建一个jquery对象，这样就能够顺利的使用jquery的方法了
                        var values = jQuery.makerArray(value);
                        jQuery("option", this).each(function() {
                            //this指向每个option元素 了
                            this.selected = (jQuery.inArray(this.value, values)) >= 0 || jQuery.inArray(this.text, values >= 0);

                        })
                        if (!values.lenght) {
                            this.selectedIndex = -1;
                        }

                    } else {
                        this.value = value;
                    }
                })
            },
            html: function(value) {
                //可以获取射设置
                return value === undefined ? (this[0] ? this[0].innerHTML : null) : this.empty().append(value);
            }，
            replaceWith: function(value) {
                //在匹配的每个元素之后插入value作为兄弟节点，并调用remove方法将value内的类似"<>"的符号去掉
                return this.after(value).remove();
            },
            eq: function(i) {
                return this.slice(i, i + 1);
            },
            slice: function() {
                return this.pushStack(Array.prototype.slice.apply(this, arguments));
            },
            map: function(callBack) {
                /*
                    这里的map调用了jQuery.map的静态方法来完成任务，它返回的是一个数组，
                    这里返回的确实一个jquery对象
                */
                return this.pushStack(jQuery.map(this, function(elem, i) {
                    return callBack.call(elem, i, elem);
                }));
            },
            andSelf: function() {
                return this.add(this.prevObject);
            },
            data: function(key, value) {
                var parts = key.split(".");
                parts[1] = parts[1] ? "." + parts[1] : "";
                if (value === undefined) {
                    var data = this.triggerHandler("getData" + parts[i] + "!", [parts[0]]);
                    if (data === undefined && this.length) {
                        data = jQuery.data(this[0], key);
                    }
                    return data === undefined && parts[1] ? this.data(parts[0]) : data;
                } else {
                    return this.trigger("setData" + parts[1] + "!", [parts[0]], value).each(function() {
                        jQuery.data(this, key, value);
                    })
                }
            },
            removeData: function(key) {
                return this.each(function() {
                    jQuery.removeData(this, key);
                })
            },
            /*
                对args进行处理，爆照args在dom内的正确性
                另外保证rgs内的脚步能在具体的dom结构生成之后才执行，避免出错
            */
            // true  //false   //function(){}
            domManip: function(args, table, reverse, callback) {
                //args是类似这样的字符串"<b>bbbbbbbb</b>"
                //如果length>1，就要clone
                var clone = this.length > 1,
                    elems;
                //遍历jquery对象（this）匹配元素集合中的每个元素，并调用匿名函数对集合内的每个元素处理
                //处理的内容是:为每个内容进行callback的操作，参数是args
                //例如
                //如果callback的功能是为元素追加内容(即是append) 则args就是要追加的具体内容
                return this.each(function() {
                        if (!elems) {
                            elems = jQuery.clean(args, this);
                            /*
                            jQuery.clean之后 elems变成一个dom数组，这些dom元素由args内表示XHTML的

                            */
                            if (reverse) {
                                elems.reverse();
                            }
                        }
                        var obj = this;
                        //为了方便叙述下面的代码分析，这里标记[1],注意
                        //这里的this是dom元素的引用 不是jquery对象的引用


                        /**
                            在IE中如果需要在table中操作tr（如插入一个tr）
                            IE要求你在tbody内操作
                            以下的if 就是说在tbody内进行操作，
                            如果没有tbody 就自己建一个，然后再操作

                        */
                        if (table && jQuery.nodeName(this, "table") && jQuery.nodeName(elems[0], "tr")) {
                            obj = this.getElementsByTagName("tbody")[0] || this.appendChild(this.owerDocument.createElement("tbody"));


                            //如果发现操作（如插入操作）的内容中含有脚本
                            //先将这些脚步装入这个集合，最后才来运行他们
                            var scripts = jQuery([]);
                            jQuery.each(elems, function() {
                                    //clone true的意思是说将绑定在jQuery(this)上的时间一并克隆
                                    var elem = clone ? jQuery(this).clone(true)[0] : //clone返回的是一个jQuery对象，这对象的匹配元素集合内
                                        this; //只有一个元素所有clone(true)就是this的一个副本
                                    //如果插入的内容里面含有script，用集合先把script装起来
                                    //等到最后所有的内容都插入完毕再执行所有的脚步
                                    if (jQuery.nodeName(elem, "script")) {
                                        scripts = scripts.add(elem);
                                    } else {
                                        if (elem.nodeType == 1) {
                                            scripts = script.add(jQuery("script", elem).remove());
                                            /*
                                                这里的obj要么是上面[1]处的this(obj=this)
                                                要么是this内的tbody
                                                最后在obj上执行callback制定的操作类型(elem)为参数

                                            */
                                            /*
                                                再次补充由于callback.call(obj,elem)使用了obj作为函数调用上下文
                                                故，obj自然成为了this所指向的对象
                                                又于是，给domMainp传入一个callback只有一个形式参数elem
                                            */
                                            callback.call(obj, elem);
                                        }
                                    }
                                }) //end each
                                //为一个元素踩入完一个内容后(即注入完HTML)，就执行刚刚保存的脚步

                        }
                        scripts.each(evalScript);

                    }) //end each
            };

        };
        //通过这句之后，jquery.fn.in it也能实例化一个jquery对象的对象了
        jQuery.fn.init.prototype = jQuery.fn;

        function evalScript(i, elem) {
            if (elem.src) {
                jQuery.ajax({
                    url: elem.src,
                    async: false,
                    dataType: "script"
                })
            } else {
                jQuery.globalEval(elem.text || elem.textContent || elem.innerHTML || "");
            }
            if (elem.parentNode) {
                elem.parentNode.removeChild(elem);
            }

        }

        function now() {
            return +new Date;
        }
        /*
            这是jquery核心中很重要的一个函数通过他就可以很轻松的在jquery或者jquery对象撒花姑娘拓展自己想要的方法
        */
        jQuery.extend = jQuery.fn.extend = function() {
                var target = arguments[0] || {}, //target是被拓展的对象,默认第一个参数(下表为0)或者是一个空对象{}
                    i = 1, //i是一个指针，它指向一个拓展对象，也就是说要把这个对象的属性或方法拓展到被拓展的对象上
                    length = arguments.length, //参数的长度，通过长度来判断拓展的模式
                    deep = false, //睡觉哦发肾拷贝
                    options; //当前正则拷贝的拓展对象的引用
                if (target.constructor == Boolean) {
                    deep = target; //保存target的布尔值
                    target = arguments[1] || {}; //让target指向我们要拷贝的对象
                    //arguments[0] arguments[1]已经得到处理 身下的就是arguments[2] arguemnt[3].......
                    i = 2;
                }
                //如果target （第二个参数）不是object 并且也不是function 就默认设置ta为{}
                if (typeof target != "object" && typeof target != "function") {
                    target = {};
                }
                /*
                    A.如果只传入一个参数，那么拓展的就是jquery自身：
                     如果使用jQuery.extend来拓展，那么this就是jquery
                     这样的话参数中的函数就作为jquery的静态方法
                    B.如果使用jQuery.fn.extend来拓展，this指的就是jquery.fn
                    参数中的函数或者属性就是作为jquery对象的属性的方法或者属性
                */
                if (length == i) {
                    target = this;
                    --i;
                }
                for (; i < length; i++) {
                    //只有哪些null的拓展对象才能把ta拓展到对象上来
                    if ((options = arguments[i]) != null) {
                        //将拓展对象(base object)，将options内的属性或方法拓展到被拓展的对象中来
                        for (var name in options) {
                            //现在要遍历每一个加进来的方法或者属性
                            //options是拓展对象，ta的方法或者属性会被拓展到target上来
                            var src = target[name],
                                copy = options[name];
                            /*
                                说明要加进来的引用是指向自己的，这在要进行的深拷贝时候就要糟糕了，所以碰到这样的情况就要跳过
                                不要把自己的应用作为自己的成员
                                不过小猫很好奇神马情况才会出现这样狗熊的情况
                            */
                            if (target === copy) {
                                continue;
                            }
                            /*
                                使用地柜实现深拷贝
                                这个条件是说：要是没有nodeType，就是飞dom对象引用，可以对他进行神拷贝
                                为啥DOM对象不能深拷贝么？？不懂
                            */
                            if (deep && copy && typeof copy == "object" && !copy.nodeType) {
                                target[name] = jQuery.extend(deep, src || (copy.length != null ? [] : {}), copy);
                            } else if (copy != undefined) {
                                target[name] = copy;
                            }

                        }
                    }
                }
                //把拓展好的对象返回
                return target;
            }
            //////////////////============================定义好了extend和jquery.fn.extend方法后，有就是用这个方法来拓展jquery方法了
        var expando = "jQuery" + now(),
            //当元素需要缓存数据时候，这样使用expandow:id=elem[expando];
            //data=jquery.catche[id]
            //如果一个元素需要缓存，那么uuid++就回成为它的缓存区全局唯一编号
            uuid = 0,

            windowData = {}, //这是数据缓存区
            //一下css是不需要加单位"px"的
            exclude = /z-?index|font-?weight|opcity|zoom|line-?height/i;
        //cache defaultView
        //这这里定义一个defaultView在需要的函数里就可以直接调用而不需要写一长串document.xxx
        defaultView = document.defaultView || {};




        //----------------jquery静态核心函数
        /*
            这些静态函数为jqueyr对象实例方法或者其他需要的函数所调用，jquery很对的实例方法实际上是大脚用了这里定义的方法完成任务的

            通过extend这个巨大的函数调用，很多jqueyr核心函数被定义并加入了jquery这个命名空间

            ///注意这些函数都是静态的，以为你只能调用他们的时候必须使用完整的限定VU好如：jquery.noconfilct()，不能像jquery实例方法那样调用ta

        */
        jQuery.extend({
                /*
                //将命名空间￥归还，调用这个函数不能再使用jquey或者$
                    @param 
                */
                noConfilct: function(deep) {
                    widnow.$ = _$; //刚刚保存起来的$归还给window
                    if (deep) {
                        //如果传入deep说明，连jquery这个关键的使用权也要放弃
                        window.jQuery = _jQuery；
                    }
                    return jQuery;
                },
                /*
                    测试一个对象是不是Function

                */
                isFunction: function(fn) {
                    /*
                        typeof 测试是否是fn在IE5.5+ ff3 safari3.0.1 chrome,opera9.62上均正常，这里为什么需要写这么多，不知道为啥估计是考虑默写奇奇怪怪的爷爷级浏览器
                            所以typeof可以大胆的使用

                        这里使用了将函数转换为字符串(使用 函数对象+"") 的方法，然后用正则表达式测试是否符合一个函数应有的模式*
                        以下情况会造成混淆
                        *1.一个内容为"function(){}"的字符串
                        *2.一个患有浑雄啊字符串的数组如:['function','()','{}']这个数组一转化成字符串，就成为了(1)情况
                    */
                    return !!fn && typeof fn！ = "string" && ！fn.nodeName && fn.constructor != Array && /^[\s[]]?function/.test(fn + "");
                },
                /*
                    检查一个元素是否是XML的document
                */
                ,
                isXMLDoc: function(elem) {
                    /*
                        body生意HTMLDocument特有的节点，常用这个document是不是一个XML文档引用

                        ///注意，HTMLDocument接口是XMLDocument的拓展，即HTMLDocument中特定处理HTML文档的方法
                            （如documentById等）是不能用在XML文档中的


                    */
                    return elem.documentElement && !elem.body || elem.tagName && elem.ownerDocument && !elem.owerDocument.body;

                },
                /*
                    在全局作用域运行脚本
                */
                globalEval: function(data) {
                    //调用strm函数取出data两头空格
                    data = jQuery.trim(data);
                    if (data) {
                        var head = document.getElementsByTagName("head")[0] || document.documentElement,
                            script = document.createElement("script");
                        script.type = "text/javascript";
                        if (jQuery.browser.mise) {
                            script.text = data;
                        } else {
                            script.appendChild(document.createTextNode(data));
                        }
                        head.insertBefore(script, head.firstChiild);
                    }
                },
                /*
                    判断一个元素的nodeName是不是给定的name
                    *elem--要庞大的元素、
                    *name-看看elem.nodeName是不是这个name
                */
                nodeName: function() {
                    return elem.nodeName && elem.nodeName.toUppperCase() == name.toUpperCase();
                },
                /*
                    全局数据缓存区，
                    每个需要缓存的数据元素都会在这里开票一个空间存自己的数据
                */
                cache: {},
                /*
                    所谓的"数据缓存"不过是个对象，存有一些数据而已
                    @param {object} elem 要在这个元素上存放数据
                    @param {object} name 数据键名
                    @param {object}  data 数据的键值
                    
                */
                data: function(elem, name, data) {
                    elem = elem == widnow ? windowData : elem;
                    /*
                        获取元素的id，这个ID存放在一个叫expando的属性里
                        expando只是由一个"jquery"+now()组成的字符串
                        他将作为一个elem的属性，同时这个属性的值也是全局数据缓存区中某一块的名字，根据这个名字
                        可以找到元素对应的缓存数据
                    */
                    var id = elem[expando];
                    //如果还没有expando编号，就给他新建一个
                    if (!id) {
                        id = elem[expando] = ++uuid;
                    }

                    if (name && jQuery.cache[id]) {
                        jQuery.cache[id] = {};
                    }
                    //undefined不允许进来
                    if (data !== undefined) {
                        jQuery.cache[id][name] = data;
                    }
                    return name ? jQuery.cache[id][name] : id;

                },
                /*
                    取消元素的缓存数据
                    @param {object} elem
                    @param {object} name
                */
                removeData: function(elem, name) {
                    elem = elem == widnow ? windowData : elem;
                    //获取元素的全局id
                    var id = elem[expando];
                    if (name) {
                        name = "";

                        for (name in jQuery.cache[id]) {

                            break;
                        }

                        if (!name) {
                            /*
                                数据缓存删掉了，地柜调用removeData,这时，removeData函数执行的就是下面那个else里面的函数了
                            */
                            jQuery.removeData(elem);
                        }
                    } else {
                        /*
                            删除元素上的expando属性，先用delete操作符删除这个元素的expando属性，如果出问题，
                            那问题可能是当前浏览器IE所致，在catch中尝试removeAttribute
                            
                            removeAttribute方法属于w3c标准中1级API，现在浏览器中已经广泛支持
                            如果不是老式浏览器可以支持跳过try/catch

                            delete操作符号属于javascript核心操作，也就是所主要是个javascript就不应该不认识delete
                            因此下面try/catch先用delete操作符，不行就用removeAttribute
                        */
                        try {
                            /*注意delete操作符只是删除元素的属性而已，它并不会三层属性所应用的内存地址的内容，回收内存空间那是垃圾回回收机制的事情

                                @important delete可以删除一个属性，但是不能删除由var定义的变量，bug隐式定义的可以删除

                            */
                            delete elem[expando];
                        } catch {
                            if (elem.removeAttribute) elem.removeAttribute(expando);
                        }
                        delete jQuery.cache[id];
                    }
                },
                /*
                    遍历
                    @param {object} object 要遍历的数组或者类数组 或者一个普通对象
                    @param {object} callback    遍历object时候执行的处理函数
                    @param {object} arg     要给callback 内部使用的参数

                */
                each: function(object, callback, args) {
                    var name, i = 0;
                    length = object.length;
                    if (arg) {
                        if (length == undefined) {
                            //没有长度，意思不是数组和类数组
                            for (name in object) {
                                //如果callback在处理某个属性后返回false，就不用对后面的属性进行处理了，为啥一定要返回false
                                if (callback.apply(object[name], args) == false) {
                                    break;
                                } else {
                                    /*
                                        else 的话，说明object是类数组的对象(大部分下是jquery对象),那就可以用下标来访问
                                    */
                                    for (; i < length;) {
                                        if (callback.apply(object[i++], args]) == false) {
                                        break;
                                    }
                                }
                            }
                        }

                    }
                    /*没有args的时候,感觉是可以和上面一样的，但是jquery有点任性 O(∩_∩)O*/
                    else {
                        if (lenght == udnefined) {
                            for (name in object) {
                                if (callback.call(object[name], name, object[name]) == false) {
                                    break;
                                }
                            }
                        } else {
                            for (var value = object[0]; i < length && callback.call(value, i, value) !== false; value == object[++i]) {

                            }
                        }

                    }
                }
                return object; //方便链式调用
            },
            /*
                对属性进行处理，取得正确的值，如这个属性值是否要加上单位px
                    elem--dom元素对象
                    value-----属性值
                    type--如果有值就代表是样式或者属性名
                    i--------dom元素在jquery对象匹配元素集合中的索引
                    name----属性名

            */
            prop: function(elem, value, type, i, name) {
                //如果这个属性值是fuction，就再elem上调用这个funciton作为函数(i 作为参数)，function处理的结果作为value
                if (jQuery.isFunction(value)) {
                    value = value.call(elem, i);
                }
                return value && value.constructor == Number && type == "curCSS" && !exclude.test(name) ? value + "px" : value;
            },
            /*
                jQuery.className命名空间，在这个命名空间上定义了一些了用来操作元素className的属性的方法
                不过这个命名空间并不对外，只是内部使用，对外开放的方法都是这些方法的包装
        
            */
            className: {
                className: function(elem, className) {
                    jQuery.each((className || "").split(/\s+/), function(i, className) {
                        if (elem.nodeType == 1 && !jQuery.className.has(elem.className, className)) {
                            elem.className += (elem.className ? " " : "") + className;
                        }
                    })
                },
                /*
                    去掉某个元素的className
                    @param {object} elem
                    @param {object} className

                */
                remove: function(elem, classNames) {
                    /*
                        注意下面的代码中的jquery.grep也是一个filter函数来的，可以参考jQuery.grep的中文注析

                    */
                    if (elem.nodeType == 1) {
                        /*
                            
                        */
                        elem.className == classNames != undefined ? jQuery.grep(elem.className.split(/\s+/), function(className) {
                            return !jQuery.className.has(classNames, className).join(" "): "";
                        })
                    }
                },
                /*
                看看elem的类名中，偶没有className指定的类名
                内部使用不对外
        
            */
                has: function(elem, className) {
                    /*
                        将elem的className，用" "切成一个数组，然后jquery.inArray看看在不在里面

                    */
                    return jQuery.inArray(className, (elem.className || elem).toString().split(/\s+/)) > -1;
                },
                /*
                    如果一个元素不处于一个状态，那么直接获取它的谋杀属性的时候会是不正确的，如offsetWidth/offsetHeight
                    在元素不可见的时候，直接获取它的值是不正确的
                    *因此想将元素设置visibilty设置为"absolute" visiblility:"hidden" display:"block";
                    *然后计算索要的样式，最后将原来的样式回复回去
                    *@param {HTMLdom} elem普通的dom元素
                    *@param {Object} options 对象，里面装着一些样式键值对，用来设置计算样式的环境
                    *@param {Fuction} callback 这个函数在设置环境执行后，当他执行完后，元素的样式会被重置

                    *
                */
                swap: function(elem, options, callback) {
                    var old = {};
                    //将旧的样式保存起来，换上新的
                    for (var name in options) {
                        old[name] = elem.style[name];
                        elem.style[name] = options[name];
                    }
                    //换上行样式后，赶紧干活，获取想要的样式的值，同时注意这些值，在没有换上新属性前，是无法正确获取
                    callback.call(elem);
                    //恢复之前的样式
                    for (var name in options) {
                        elem.style[name] = old[name];
                    }
                },
                /*
                    获取元素的当前css样式值
                    @parm {HTMLElement} elem 元素
                    @param {string}  样式属性名
                    @param {object} force

                */
                css: function(elem, name, force) {
                    /*
                        如果要获取width或者height属性，需要特殊处理:
                        不是直接获取width或者height属性，而是使用了offsetWidth/offsetHeight，这么获取width/height
                        这需要裁剪,因为border和padding的问题

                    */
                    if (name == "width" || name == "height") {
                        var val, //这是最后要返回的结果
                            //这是待会要调用jQuery.swap函数的，具体看下面代码或者常考jquery。swap
                            props = { position: "absolute", visibility: "hidden", display: "block" },
                            which = name == "width" ? ["Left", "Right"] : ["Top", "Bottom"];

                        function getWH() {
                            val = name == "width" ? elem.offsetWidth : elem.offsetHeight;
                            var padding = 0,
                                border = 0;
                            jQuery.each(which, function() {
                                padding += parseFloat(jQuery.curCSS(elem, "padding" + this, true)) || 0;
                                border += parseFloat(jQuery.curCSS(elem, "border" + this + "Width", true)) || 0;
                            })
                            val-=Math.round(padding+border);
                            if(jQuery(elem).is(":visible")){
                                getWH();
                            }
                            /*
                                    如果没有visible并不能正确的获取到width,为了反正出乱子，调用jQuery.swap函数线将给元素设置这样的属性
                                     { position: "absolute", visibility: "hidden", display: "block" }
                                     再获取，这样就十拿九稳了，
                            */
                            else{
                                jQuery.swap(elem,props,getWH);
                            }
                            //width height不给设置-值
                            return Math.max(0.val);
                        }
                    }
                    return jQuery.curCSS( elem, name, force );
                }












            }


        })




});
